package shop_cart

import (
	"context"
	"net/http"
	"sort"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"

	"mxshop-api/order-web/api"
	"mxshop-api/order-web/forms"
	"mxshop-api/order-web/global"
	goodspb "mxshop-api/order-web/proto/goods/gen/v1"
	inventorypb "mxshop-api/order-web/proto/inventory/gen/v1"
	orderpb "mxshop-api/order-web/proto/order/gen/v1"
	"mxshop-api/shared/auth"
	"mxshop-api/shared/utils"
)

// CartItemSlice 购物车商品排序
type CartItemSlice []*orderpb.CartItemSimpleEntity

func (c CartItemSlice) Len() int      { return len(c) }
func (c CartItemSlice) Swap(i, j int) { c[i], c[j] = c[j], c[i] }

// CartItemByGoodsID 根据购物车商品ID进行排序
type CartItemByGoodsID struct{ CartItemSlice }

func (c CartItemByGoodsID) Less(i, j int) bool {
	return c.CartItemSlice[i].GoodsId < c.CartItemSlice[j].GoodsId
}

// GoodsInfoSlice 商品信息排序
type GoodsInfoSlice []*goodspb.GoodsInfoResponse

func (g GoodsInfoSlice) Len() int      { return len(g) }
func (g GoodsInfoSlice) Swap(i, j int) { g[i], g[j] = g[j], g[i] }

// GoodsInfoByID 根据商品ID进行排序
type GoodsInfoByID struct{ GoodsInfoSlice }

func (g GoodsInfoByID) Less(i, j int) bool {
	return g.GoodsInfoSlice[i].Id < g.GoodsInfoSlice[j].Id
}

func List(c *gin.Context) {
	// 1.获取登录的用户信息
	userID, err := auth.UserIDFromContext(c)
	if err != nil {
		return
	}

	// 2.获取用户的购物车商品信息
	orderCli := orderpb.NewOrderServiceClient(global.OrderConn)
	cartItemListResp, err := orderCli.CartItemList(context.Background(), &orderpb.CartItemListRequest{UserId: int32(userID)})
	if err != nil {
		api.HandleGrpcErrorToHttp(c, err)
		return

	}
	cartItemSlice := cartItemListResp.Data

	// 如果cartItemSlice为空表示购物车中没有商品,可以直接返回。
	if len(cartItemSlice) == 0 {
		c.JSON(http.StatusOK, gin.H{"total": 0})
		return
	}

	// 排序以便后续组合响应内容
	sort.Sort(CartItemByGoodsID{cartItemSlice})

	ids := make([]int32, 0, len(cartItemSlice)) // 存放批量查询商品信息的id列表
	for _, item := range cartItemListResp.Data {
		ids = append(ids, item.GoodsId)
	}

	// 3.查询商品信息
	goodsCli := goodspb.NewGoodsClient(global.GoodsConn)
	goodsListResp, err := goodsCli.BatchGetGoods(context.Background(), &goodspb.BatchGoodsIdInfo{Id: ids})
	if err != nil {
		api.HandleGrpcErrorToHttp(c, err)
		return
	}

	goodsInfoSlice := goodsListResp.Data

	if len(cartItemSlice) != len(goodsInfoSlice) {
		zap.L().Error("购物车商品数量错误", zap.Int32("user_id", int32(userID)))
		c.JSON(http.StatusInternalServerError, gin.H{"error": "请稍后重试"})
		return
	}

	// 排序以便后续组合响应内容
	sort.Sort(GoodsInfoByID{goodsInfoSlice})

	// 4.组合响应内容
	/*
		{
			"total": 10,
			"data": [
				{
					"id": 购物车项的ID,
					"goods_id": 商品的ID,
					"goods_name": 商品名称,
					"goods_price": 商品价格,
					"goods_image": 商品图片,
					"nums": 购买商品的数量,
					"checked": 是否有选中,
				},
				...
			]
		}
	*/
	data := make([]gin.H, 0, len(cartItemSlice))
	for i := 0; i < len(cartItemSlice); i++ {
		if cartItemSlice[i].GoodsId != goodsInfoSlice[i].Id {
			zap.L().Error("排序后的购物车商品id与商品服务的商品id不正确", zap.Int32("user_id", int32(userID)))
			c.JSON(http.StatusInternalServerError, gin.H{"error": "请稍后重试"})
			return
		}

		data = append(data, gin.H{
			"id":          cartItemSlice[i].Id,
			"goods_id":    cartItemSlice[i].GoodsId,
			"goods_name":  goodsInfoSlice[i].Name,
			"goods_price": utils.PriceInt(goodsInfoSlice[i].ShopPrice).String(),
			"goods_image": goodsInfoSlice[i].GoodsFrontImage,
			"nums":        cartItemSlice[i].Nums,
			"checked":     cartItemSlice[i].Checked,
		})
	}
	c.JSON(http.StatusOK, gin.H{
		"total": len(data),
		"data":  data,
	})
}

type APICartItem struct {
	CartItemID int32 `json:"cart_item_id"`
	Nums       int32 `json:"nums"`
}

func New(c *gin.Context) {
	userID, err := auth.UserIDFromContext(c)
	if err != nil {
		return
	}

	// 1.获取并校验参数
	var cartItem forms.ShopCartItem
	err = c.ShouldBind(&cartItem)
	if err != nil {
		api.HandleValidatorError(c, err)
		return
	}

	// 2.为了严谨性，添加商品到购物车之前，记得检查一下商品是否存在
	goodsCli := goodspb.NewGoodsClient(global.GoodsConn)
	goods, err := goodsCli.GetGoodsDetail(context.Background(), &goodspb.GoodInfoRequest{Id: cartItem.GoodsId})
	if err != nil {
		api.HandleGrpcErrorToHttp(c, err)
		return
	}

	// 3.如果现在添加到购物车的数量和库存的数量不一致
	inventoryCli := inventorypb.NewInventoryClient(global.InventoryConn)
	invInfoResp, err := inventoryCli.InvDetail(context.Background(), &inventorypb.InvDetailRequest{GoodsId: cartItem.GoodsId})
	if err != nil {
		api.HandleGrpcErrorToHttp(c, err)
		return
	}
	if invInfoResp.Num < cartItem.Nums {
		c.JSON(http.StatusBadRequest, gin.H{
			"nums": "库存不足",
		})
		return
	}

	// 4.添加到购物车
	orderCli := orderpb.NewOrderServiceClient(global.OrderConn)
	item, err := orderCli.CreateCartItem(context.Background(), &orderpb.CartItem{
		UserId:     int32(userID),
		GoodsId:    cartItem.GoodsId,
		GoodsName:  goods.Name,
		GoodsImage: goods.GoodsFrontImage,
		GoodsPrice: goods.ShopPrice,
		Nums:       cartItem.Nums,
		Checked:    false,
	})
	if err != nil {
		api.HandleGrpcErrorToHttp(c, err)
		return
	}

	var apiCartItem = APICartItem{
		CartItemID: item.Id,
		Nums:       item.Nums,
	}
	c.JSON(http.StatusCreated, apiCartItem)
}

func Update(c *gin.Context) {
	userID, err := auth.UserIDFromContext(c)
	if err != nil {
		return
	}

	// 获取需要修改购物车项的ID
	var i forms.IDValidator
	err = c.ShouldBindUri(&i)
	if err != nil {
		api.HandleValidatorError(c, err)
		return
	}

	// 获取需要修改的内容
	var itemUpdateForm forms.ShopCartItemUpdateForm
	err = c.ShouldBindJSON(&itemUpdateForm)
	if err != nil {
		api.HandleValidatorError(c, err)
		return
	}

	// 调用order service进行更改
	orderCli := orderpb.NewOrderServiceClient(global.OrderConn)

	var itemEntity = &orderpb.CartItemSimpleEntity{
		Id:     i.ID,
		UserId: int32(userID),
		Nums:   itemUpdateForm.Nums,
	}
	if itemUpdateForm.Checked != nil {
		itemEntity.Checked = *itemUpdateForm.Checked
	}

	_, err = orderCli.UpdateCartItem(context.Background(), itemEntity)
	if err != nil {
		api.HandleGrpcErrorToHttp(c, err)
		return
	}

	c.Status(http.StatusCreated)
}

func Delete(c *gin.Context) {
	userID, err := auth.UserIDFromContext(c)
	if err != nil {
		return
	}

	// 获取购物车项的ID
	var i forms.IDValidator
	err = c.ShouldBindUri(&i)
	if err != nil {
		api.HandleValidatorError(c, err)
		return
	}

	orderCli := orderpb.NewOrderServiceClient(global.OrderConn)
	_, err = orderCli.DeleteCartItem(context.Background(), &orderpb.DeleteCartItemRequest{
		Id:     i.ID,
		UserId: int32(userID),
	})
	if err != nil {
		api.HandleGrpcErrorToHttp(c, err)
		return
	}

	c.Status(http.StatusNoContent)
}
